﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace OseaCS
{
    class Match
    {
        /*****************************************************************************
        FILE:  match.cpp
        AUTHOR:	Patrick S. Hamilton
        REVISED:	5/13/2002
          ___________________________________________________________________________

        match.cpp: Match beats to previous beats.
        Copywrite (C) 2001 Patrick S. Hamilton

        This file is free software; you can redistribute it and/or modify it under
        the terms of the GNU Library General Public License as published by the Free
        Software Foundation; either version 2 of the License, or (at your option) any
        later version.

        This software is distributed in the hope that it will be useful, but WITHOUT ANY
        WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
        PARTICULAR PURPOSE.  See the GNU Library General Public License for more
        details.

        You should have received a copy of the GNU Library General Public License along
        with this library; if not, write to the Free Software Foundation, Inc., 59
        Temple Place - Suite 330, Boston, MA 02111-1307, USA.

        You may contact the author by e-mail (pat@eplimited.edu) or postal mail
        (Patrick Hamilton, E.P. Limited, 35 Medford St., Suite 204 Somerville,
        MA 02143 USA).  For updates to this software, please visit our website
        (http://www.eplimited.com).
          __________________________________________________________________________

        Match.cpp contains functions for managing template matching of beats and
        managing of feature data associated with each beat type.  These
        functions are called functions in classify.cpp.  Beats are matched to
        previoiusly detected beats types based on how well they match point by point
        in a MATCH_LENGTH region centered on FIDMARK (R-wave location).  The following
        is a list of functions that are available for calling by classify.

	        ResetMatch -- Resets global variables used in template matching.
	        CompareBeats -- Measures the difference between two beats with
							        beats scaled to produce the best match.
	        CompareBeats2 -- Measures the difference between two beats without
							        beat scaling.
	        NewBeatType -- Start a new beat type with the present beat.
	        BestMorphMatch -- Finds the beat template that best matches a new beat.
	        UpdateBeatType -- Updates existing beat template and associated features
							        based on a new beat.
	        GetDominantType -- Returns the NORMAL beat type that has occorred most often.
	        ClearLastNewType -- Removes the last new beat type from the possible beat
							        types.
	        DomCompare -- Compares the template for a given beat type to the template
						        of the dominant normal beat type.
	        DomCompare2 -- Compares a given beat template to the templat of the
						        dominant normal beat type.

	        PostClassify -- Classifies beats based on preceding and following beats
						        and R-to-R intervals.

	        ResetPostClassify -- Resets variables used for post classification.

	        CheckPostClass -- Check type classification based on last eight post
						        classifications.

	        CheckPCClass -- Check post beat rhythm classification for the last eight
						        beats.

        A number of simple functions allow access to beat features while maintaining
        some level of encapsulation:

	        GetTypesCount -- Returns number of beat types that have been detected.
	        GetBeatTypeCount -- Returns the number of beats of a given type
							          that have been detected.
	        GetBeatWidth -- Returns the width estimate for a given beat type.
	        SetBeatClass -- Associates a beat classification with a beat type.
	        GetBeatBegin -- Returns the beginning point for a given beat type.
	        GetBeatEnd -- Returns the ending point for a given beat type.

        ******************************************************************************/
        //#include <stdlib.h>
        //#include <stdio.h>
        //#include "ecgcodes.h"
        //#include "bdac.h"

        public static int MATCH_LENGTH = BdacH.BEAT_MS300;	// Number of points used for beat matching.
        public static double MATCH_LIMIT = 1.2;			// Match limit used testing whether two beat types might be combined.
        public static double COMBINE_LIMIT = 0.8;		// Limit used for deciding whether two types can be combined.

        public static int MATCH_START = (BdacH.FIDMARK-(MATCH_LENGTH/2));	// Starting point for beat matching
        public static int MATCH_END = (BdacH.FIDMARK+(MATCH_LENGTH/2));		// End point for beat matching.
        public static int MAXPREV = 8;	                // Number of preceeding beats used as beat features.
        public static int MAX_SHIFT = BdacH.BEAT_MS40;

        // Local prototypes.
        //int NoiseCheck(int *beat) ;
        //double CompareBeats(int *beat1, int *beat2, int *shiftAdj) ;
        //double CompareBeats2(int *beat1, int *beat2, int *shiftAdj) ;
        //void UpdateBeat(int *aveBeat, int *newBeat, int shift) ;
        //void BeatCopy(int srcBeat, int destBeat) ;
        //int MinimumBeatVariation(int type) ;

        //// External prototypes.
        //void AnalyzeBeat(int *beat, int *onset, int *offset, int *isoLevel,
        //int *beatBegin, int *beatEnd, int *amp) ;
        //void AdjustDomData(int oldType, int newType) ;
        //void CombineDomData(int oldType, int newType);

        // Global variables.
        int[,] i = new int[2,3];

        public static int[,] BeatTemplates = new int[BdacH.MAXTYPES, BdacH.BEATLGTH];
        public static int[] BeatCounts = new int[BdacH.MAXTYPES];
        public static int[] BeatWidths = new int[BdacH.MAXTYPES];
        public static int[] BeatClassifications = new int[BdacH.MAXTYPES];
        public static int[] BeatBegins = new int[BdacH.MAXTYPES];
        public static int[] BeatEnds = new int[BdacH.MAXTYPES];
        public static int[] BeatsSinceLastMatch = new int[BdacH.MAXTYPES];
        public static int[] BeatAmps = new int[BdacH.MAXTYPES];
        public static int[] BeatCenters = new int[BdacH.MAXTYPES];
        public static double[,] MIs = new double[BdacH.MAXTYPES, 8];

        // Need access to these in postclas.cpp when beat types are combined
        // and moved.

        public static int[,] PostClass = new int[BdacH.MAXTYPES, 8];
        public static int[,] PCRhythm = new int[BdacH.MAXTYPES, 8];

        public static int TypeCount = 0;

        /***************************************************************************
        ResetMatch() resets static variables involved with template matching.
        ****************************************************************************/

        public static void ResetMatch()
	    {
	        int i, j ;
	        TypeCount = 0 ;
	        for(i = 0; i < BdacH.MAXTYPES; ++i)
		    {
		        BeatCounts[i] = 0 ;
		        BeatClassifications[i] = EcgCodes.UNKNOWN ;
		        for (j = 0; j < 8; ++j)
			    {
			        MIs[i, j] = 0;
                }
            }
        }


        /**************************************************************************
	        CompareBeats() takes two beat buffers and compares how well they match
	        point-by-point.  Beat2 is shifted and scaled to produce the closest
	        possible match.  The metric returned is the sum of the absolute
	        differences between beats divided by the amplitude of the beats.  The
	        shift used for the match is returned via the pointer *shiftAdj.
        ***************************************************************************/

        //#define MATCH_START	(FIDMARK-(MATCH_LENGTH/2))
        //#define MATCH_END	(FIDMARK+(MATCH_LENGTH/2))

        unsafe static double CompareBeats(int* beat1, int* beat2, int* shiftAdj)
        {
            int i, max, min, magSum, shift;
            long beatDiff, meanDiff, minDiff, minShift;
            double metric, scaleFactor, tempD;
            beatDiff = meanDiff = minDiff = minShift = 0; //TODO: inseri a inicializacao das variaveis

            // Calculate the magnitude of each beat.

            max = min = beat1[MATCH_START];
            for (i = MATCH_START + 1; i < MATCH_END; ++i)
                if (beat1[i] > max)
                    max = beat1[i];
                else if (beat1[i] < min)
                    min = beat1[i];

            magSum = max - min;

            i = MATCH_START;
            max = min = beat2[i];
            for (i = MATCH_START + 1; i < MATCH_END; ++i)
                if (beat2[i] > max)
                    max = beat2[i];
                else if (beat2[i] < min)
                    min = beat2[i];

            // magSum += max - min ;
            scaleFactor = magSum;
            scaleFactor /= max - min;
            magSum *= 2;

            // Calculate the sum of the point-by-point
            // absolute differences for five possible shifts.

            for (shift = -MAX_SHIFT; shift <= MAX_SHIFT; ++shift)
            {
                for (i = BdacH.FIDMARK - (MATCH_LENGTH >> 1), meanDiff = 0; i < BdacH.FIDMARK + (MATCH_LENGTH >> 1); ++i)
                {
                    tempD = beat2[i + shift];
                    tempD *= scaleFactor;
                    meanDiff += (long) (beat1[i] - tempD); // beat2[i+shift] ;
                }
                meanDiff /= MATCH_LENGTH;

                for (i = BdacH.FIDMARK - (MATCH_LENGTH >> 1), beatDiff = 0; i < BdacH.FIDMARK + (MATCH_LENGTH >> 1); ++i)
                {
                    tempD = beat2[i + shift];
                    tempD *= scaleFactor;
                    beatDiff += (long)Math.Abs(beat1[i] - meanDiff - tempD); // beat2[i+shift]  ) ; //TODO: inseri um cast para (long)
                }


                if (shift == -MAX_SHIFT)
                {
                    minDiff = beatDiff;
                    minShift = -MAX_SHIFT;
                }
                else if (beatDiff < minDiff)
                {
                    minDiff = beatDiff;
                    minShift = shift;
                }
            }

            metric = minDiff;
            *shiftAdj = (int)minShift; //TODO: inseri um cast para (int)
            metric /= magSum;

            // Metric scales inversely with match length.
            // algorithm was originally tuned with a match
            // length of 30.

            metric *= 30;
            metric /= MATCH_LENGTH;
            return (metric);
        }


        /***************************************************************************
	        CompareBeats2 is nearly the same as CompareBeats above, but beat2 is
	        not scaled before calculating the match metric.  The match metric is
	        then the sum of the absolute differences divided by the average amplitude
	        of the two beats.
        ****************************************************************************/

        unsafe public static double CompareBeats2(int[] beat1, int* beat2, int* shiftAdj)
        {
            int i, max, min, shift;
            int mag1, mag2;
            long beatDiff, meanDiff, minDiff, minShift;
            double metric;
            beatDiff = meanDiff = minDiff = minShift = 0; //TODO: inseri a inicializacao das variaveis


            // Calculate the magnitude of each beat.

            max = min = beat1[MATCH_START];
            for (i = MATCH_START + 1; i < MATCH_END; ++i)
                if (beat1[i] > max)
                    max = beat1[i];
                else if (beat1[i] < min)
                    min = beat1[i];

            mag1 = max - min;

            i = MATCH_START;
            max = min = beat2[i];
            for (i = MATCH_START + 1; i < MATCH_END; ++i)
                if (beat2[i] > max)
                    max = beat2[i];
                else if (beat2[i] < min)
                    min = beat2[i];

            mag2 = max - min;

            // Calculate the sum of the point-by-point
            // absolute differences for five possible shifts.

            for (shift = -MAX_SHIFT; shift <= MAX_SHIFT; ++shift)
            {
                for (i = BdacH.FIDMARK - (MATCH_LENGTH >> 1), meanDiff = 0;
                    i < BdacH.FIDMARK + (MATCH_LENGTH >> 1); ++i)
                    meanDiff += beat1[i] - beat2[i + shift];
                meanDiff /= MATCH_LENGTH;

                for (i = BdacH.FIDMARK - (MATCH_LENGTH >> 1), beatDiff = 0; i < BdacH.FIDMARK + (MATCH_LENGTH >> 1); ++i)
                    beatDiff += Math.Abs(beat1[i] - meanDiff - beat2[i + shift]);

                if (shift == -MAX_SHIFT)
                {
                    minDiff = beatDiff;
                    minShift = -MAX_SHIFT;
                }
                else if (beatDiff < minDiff)
                {
                    minDiff = beatDiff;
                    minShift = shift;
                }
            }

            metric = minDiff;
            *shiftAdj = (int)minShift; //TODO: inseri um cast para (int)
            metric /= (mag1 + mag2);

            // Metric scales inversely with match length.
            // algorithm was originally tuned with a match
            // length of 30.

            metric *= 30;
            metric /= MATCH_LENGTH;

            return (metric);
        }


        /************************************************************************
        UpdateBeat() averages a new beat into an average beat template by adding
        1/8th of the new beat to 7/8ths of the average beat.
        *************************************************************************/

        unsafe public static void UpdateBeat(int[] aveBeat, int[] newBeat, int shift)
        {
            int i;
            long tempLong;

            for (i = 0; i < BdacH.BEATLGTH; ++i)
            {
                if ((i + shift >= 0) && (i + shift < BdacH.BEATLGTH))
                {
                    tempLong = aveBeat[i];
                    tempLong *= 7;
                    tempLong += newBeat[i + shift];
                    tempLong >>= 3;
                    aveBeat[i] = (int) tempLong; //TODO: inseri um cast para (int)
                }
            }
        }


        /*******************************************************
	        GetTypesCount returns the number of types that have
	        been detected.
        *******************************************************/

        public static int GetTypesCount()
        {
            return (TypeCount);
        }


        /********************************************************
	        GetBeatTypeCount returns the number of beats of a
	        a particular type have been detected.
        ********************************************************/

        public static int GetBeatTypeCount(int type)
        {
            return (BeatCounts[type]);
        }


        /*******************************************************
	        GetBeatWidth returns the QRS width estimate for
	        a given type of beat.
        *******************************************************/

        public static int GetBeatWidth(int type)
        {
            return (BeatWidths[type]);
        }


        /*******************************************************
	        GetBeatCenter returns the point between the onset and
	        offset of a beat.
        ********************************************************/

        public static int GetBeatCenter(int type)
        {
            return (BeatCenters[type]);
        }


        /*******************************************************
	        GetBeatClass returns the present classification for
	        a given beat type (NORMAL, PVC, or UNKNOWN).
        ********************************************************/

        public static int GetBeatClass(int type)
        {
            if (type == BdacH.MAXTYPES)
                return (EcgCodes.UNKNOWN);
            return (BeatClassifications[type]);
        }


        /******************************************************
	        SetBeatClass sets up a beat classifation for a
	        given type.
        ******************************************************/

        public static void SetBeatClass(int type, int beatClass)
	    {
	        BeatClassifications[type] = beatClass ;
	    }


        /******************************************************************************
	        NewBeatType starts a new beat type by storing the new beat and its
	        features as the next available beat type.
        ******************************************************************************/

        unsafe public static int NewBeatType(int* newBeat)
        {
            int i, onset, offset, isoLevel, beatBegin, beatEnd;
            int mcType, amp;

            // Update count of beats since each template was matched.
            
            for (i = 0; i < TypeCount; ++i)
                ++BeatsSinceLastMatch[i];

            if (TypeCount < BdacH.MAXTYPES)
            {
                for (i = 0; i < BdacH.BEATLGTH; ++i)
                    BeatTemplates[TypeCount, i] = newBeat[i];

                BeatCounts[TypeCount] = 1;
                BeatClassifications[TypeCount] = EcgCodes.UNKNOWN;
                AnalBeat.AnalyzeBeat(&BeatTemplates[TypeCount, 0], &onset, &offset, &isoLevel,
                                    &beatBegin, &beatEnd, &amp);
                BeatWidths[TypeCount] = offset - onset;
                BeatCenters[TypeCount] = (offset + onset) / 2;
                BeatBegins[TypeCount] = beatBegin;
                BeatEnds[TypeCount] = beatEnd;
                BeatAmps[TypeCount] = amp;

                BeatsSinceLastMatch[TypeCount] = 0;

                ++TypeCount;
                return (TypeCount - 1);
            }

            // If we have used all the template space, replace the beat
            // that has occurred the fewest number of times.

            else
            {
                // Find the template with the fewest occurances,
                // that hasn't been matched in at least 500 beats.

                mcType = -1;

                if (mcType == -1)
                {
                    mcType = 0;
                    for (i = 1; i < BdacH.MAXTYPES; ++i)
                        if (BeatCounts[i] < BeatCounts[mcType])
                            mcType = i;
                        else if (BeatCounts[i] == BeatCounts[mcType])
                        {
                            if (BeatsSinceLastMatch[i] > BeatsSinceLastMatch[mcType])
                                mcType = i;
                        }
                }

                // Adjust dominant beat monitor data.

                classify.AdjustDomData(mcType, BdacH.MAXTYPES);

                // Substitute this beat.

                for (i = 0; i < BdacH.BEATLGTH; ++i)
                    BeatTemplates[mcType, i] = newBeat[i];

                BeatCounts[mcType] = 1;
                BeatClassifications[mcType] = EcgCodes.UNKNOWN;
                AnalBeat.AnalyzeBeat(&BeatTemplates[mcType, 0], &onset, &offset, &isoLevel,
                                        &beatBegin, &beatEnd, &amp);
                BeatWidths[mcType] = offset - onset;
                BeatCenters[mcType] = (offset + onset) / 2;
                BeatBegins[mcType] = beatBegin;
                BeatEnds[mcType] = beatEnd;
                BeatsSinceLastMatch[mcType] = 0;
                BeatAmps[mcType] = amp;
                return (mcType);
            }
        }


        /***************************************************************************
	        BestMorphMatch tests a new beat against all available beat types and
	        returns (via pointers) the existing type that best matches, the match
	        metric for that type, and the shift used for that match.
        ***************************************************************************/

        unsafe public static void BestMorphMatch(int* newBeat, int* matchType, double* matchIndex, double* mi2, int* shiftAdj)
        {
            int type, i, bestMatch, nextBest, minShift, shift, temp;
            int bestShift2, nextShift2;
            double bestDiff2, nextDiff2;
            double beatDiff, minDiff, nextDiff = 10000;

            if (TypeCount == 0)
            {
                *matchType = 0;
                *matchIndex = 1000;		// Make sure there is no match so a new beat is
                *shiftAdj = 0;			// created.
                return;
            }

            // Compare the new beat to all type beat
            // types that have been saved.

            for (type = 0; type < TypeCount; ++type)
            {
                beatDiff = CompareBeats(&BeatTemplates[type, 0], newBeat, &shift);
                if (type == 0)
                {
                    bestMatch = 0;
                    minDiff = beatDiff;
                    minShift = shift;
                }
                else if (beatDiff < minDiff)
                {
                    nextBest = bestMatch;
                    nextDiff = minDiff;
                    bestMatch = type;
                    minDiff = beatDiff;
                    minShift = shift;
                }
                else if ((TypeCount > 1) && (type == 1))
                {
                    nextBest = type;
                    nextDiff = beatDiff;
                }
                else if (beatDiff < nextDiff)
                {
                    nextBest = type;
                    nextDiff = beatDiff;
                }
            }

            // If this beat was close to two different
            // templates, see if the templates which template
            // is the best match when no scaling is used.
            // Then check whether the two close types can be combined.

            if ((minDiff < MATCH_LIMIT) && (nextDiff < MATCH_LIMIT) && (TypeCount > 1))
            {
                // Compare without scaling.

                bestDiff2 = CompareBeats2(&BeatTemplates[bestMatch,0], newBeat, &bestShift2);
                nextDiff2 = CompareBeats2(&BeatTemplates[nextBest,0], newBeat, &nextShift2);
                if (nextDiff2 < bestDiff2)
                {
                    temp = bestMatch;
                    bestMatch = nextBest;
                    nextBest = temp;
                    temp = (int) minDiff;
                    minDiff = nextDiff;
                    nextDiff = temp;
                    minShift = nextShift2;
                    *mi2 = bestDiff2;
                }
                else *mi2 = nextDiff2;

                beatDiff = CompareBeats(&BeatTemplates[bestMatch,0], &BeatTemplates[nextBest,0], &shift);

                if ((beatDiff < COMBINE_LIMIT) &&
                    ((*mi2 < 1.0) || (!MinimumBeatVariation(nextBest))))
                {

                    // Combine beats into bestMatch

                    if (bestMatch < nextBest)
                    {
                        for (i = 0; i < BdacH.BEATLGTH; ++i)
                        {
                            if ((i + shift > 0) && (i + shift < BdacH.BEATLGTH))
                            {
                                BeatTemplates[bestMatch, i] += BeatTemplates[nextBest, i + shift];
                                BeatTemplates[bestMatch, i] >>= 1;
                            }
                        }

                        if ((BeatClassifications[bestMatch] == EcgCodes.NORMAL) || (BeatClassifications[nextBest] == EcgCodes.NORMAL))
                            BeatClassifications[bestMatch] = EcgCodes.NORMAL;
                        else if ((BeatClassifications[bestMatch] == EcgCodes.PVC) || (BeatClassifications[nextBest] == EcgCodes.PVC))
                            BeatClassifications[bestMatch] = EcgCodes.PVC;

                        BeatCounts[bestMatch] += BeatCounts[nextBest];

                        classify.CombineDomData(nextBest, bestMatch);

                        // Shift other templates over.

                        for (type = nextBest; type < TypeCount - 1; ++type)
                            BeatCopy(type + 1, type);

                    }

                    // Otherwise combine beats it nextBest.

                    else
                    {
                        for (i = 0; i < BdacH.BEATLGTH; ++i)
                        {
                            BeatTemplates[nextBest,i] += BeatTemplates[bestMatch, i];
                            BeatTemplates[nextBest,i] >>= 1;
                        }

                        if ((BeatClassifications[bestMatch] == EcgCodes.NORMAL) || (BeatClassifications[nextBest] == EcgCodes.NORMAL))
                            BeatClassifications[nextBest] = EcgCodes.NORMAL;
                        else if ((BeatClassifications[bestMatch] == EcgCodes.PVC) || (BeatClassifications[nextBest] == EcgCodes.PVC))
                            BeatClassifications[nextBest] = EcgCodes.PVC;

                        BeatCounts[nextBest] += BeatCounts[bestMatch];

                        classify.CombineDomData(bestMatch, nextBest);

                        // Shift other templates over.

                        for (type = bestMatch; type < TypeCount - 1; ++type)
                            BeatCopy(type + 1, type);


                        bestMatch = nextBest;
                    }
                    --TypeCount;
                    BeatClassifications[TypeCount] = EcgCodes.UNKNOWN;
                }
            }
            *mi2 = CompareBeats2(&BeatTemplates[bestMatch,0], newBeat, &bestShift2);
            *matchType = bestMatch;
            *matchIndex = minDiff;
            *shiftAdj = minShift;
        }


        /***************************************************************************
	        UpdateBeatType updates the beat template and features of a given beat type
	        using a new beat.
        ***************************************************************************/

        unsafe public static void UpdateBeatType(int matchType, int* newBeat, double mi2, int shiftAdj)
        {
            int i, onset, offset, isoLevel, beatBegin, beatEnd;
            int amp;

            // Update beats since templates were matched.

            for (i = 0; i < TypeCount; ++i)
            {
                if (i != matchType)
                    ++BeatsSinceLastMatch[i];
                else BeatsSinceLastMatch[i] = 0;
            }

            // If this is only the second beat, average it with the existing
            // template.

            if (BeatCounts[matchType] == 1)
                for (i = 0; i < BdacH.BEATLGTH; ++i)
                {
                    if ((i + shiftAdj >= 0) && (i + shiftAdj < BdacH.BEATLGTH))
                        BeatTemplates[matchType, i] = (BeatTemplates[matchType, i] + newBeat[i + shiftAdj]) >> 1;
                }

            // Otherwise do a normal update.

            else
                Match.UpdateBeat(&BeatTemplates[matchType,0], newBeat, shiftAdj);

            // Determine beat features for the new average beat.

            AnalBeat.AnalyzeBeat(&BeatTemplates[matchType, 0], &onset, &offset, &isoLevel,
                                 &beatBegin, &beatEnd, &amp);

            BeatWidths[matchType] = offset - onset;
            BeatCenters[matchType] = (offset + onset) / 2;
            BeatBegins[matchType] = beatBegin;
            BeatEnds[matchType] = beatEnd;
            BeatAmps[matchType] = amp;

            ++BeatCounts[matchType];

            for (i = MAXPREV - 1; i > 0; --i)
                MIs[matchType, i] = MIs[matchType, i - 1];
            MIs[matchType, 0] = mi2;

        }


        /****************************************************************************
	        GetDominantType returns the NORMAL beat type that has occurred most
	        frequently.
        ****************************************************************************/

        public static int GetDominantType()
        {
            int maxCount = 0, maxType = -1;
            int type, totalCount;

            for (type = 0; type < BdacH.MAXTYPES; ++type)
            {
                if ((BeatClassifications[type] == EcgCodes.NORMAL) && (BeatCounts[type] > maxCount))
                {
                    maxType = type;
                    maxCount = BeatCounts[type];
                }
            }

            // If no normals are found and at least 300 beats have occurred, just use
            // the most frequently occurring beat.

            if (maxType == -1)
            {
                for (type = 0, totalCount = 0; type < TypeCount; ++type)
                    totalCount += BeatCounts[type];
                if (totalCount > 300)
                    for (type = 0; type < TypeCount; ++type)
                        if (BeatCounts[type] > maxCount)
                        {
                            maxType = type;
                            maxCount = BeatCounts[type];
                        }
            }

            return (maxType);
        }


        /***********************************************************************
	        ClearLastNewType removes the last new type that was initiated
        ************************************************************************/

        public static void ClearLastNewType()
        {
            if (TypeCount != 0)
                --TypeCount;
        }


        /****************************************************************
	        GetBeatBegin returns the offset from the R-wave for the
	        beginning of the beat (P-wave onset if a P-wave is found).
        *****************************************************************/

        public static int GetBeatBegin(int type)
        {
            return (BeatBegins[type]);
        }


        /****************************************************************
	        GetBeatEnd returns the offset from the R-wave for the end of
	        a beat (T-wave offset).
        *****************************************************************/

        public static int GetBeatEnd(int type)
        {
            return (BeatEnds[type]);
        }

        public static int GetBeatAmp(int type)
        {
            return (BeatAmps[type]);
        }


        /************************************************************************
	        DomCompare2 and DomCompare return similarity indexes between a given
	        beat and the dominant normal type or a given type and the dominant
	        normal type.
        ************************************************************************/

        unsafe public static double DomCompare2(int[,] newBeat, int domType)
        {
            int shift;
            return (CompareBeats2(&BeatTemplates[domType, 0], newBeat, &shift));
        }

        unsafe public static double DomCompare(int newType, int domType)
        {
            int shift;
            return (CompareBeats2(&BeatTemplates[domType,0], &BeatTemplates[newType,0], &shift));
        }


        /*************************************************************************
        BeatCopy copies beat data from a source beat to a destination beat.
        *************************************************************************/

        public static void BeatCopy(int srcBeat, int destBeat)
        {
            int i;

            // Copy template.

            for (i = 0; i < BdacH.BEATLGTH; ++i)
                BeatTemplates[destBeat,i] = BeatTemplates[srcBeat,i];

            // Move feature information.

            BeatCounts[destBeat] = BeatCounts[srcBeat];
            BeatWidths[destBeat] = BeatWidths[srcBeat];
            BeatCenters[destBeat] = BeatCenters[srcBeat];
            for (i = 0; i < MAXPREV; ++i)
            {
                PostClass[destBeat,i] = PostClass[srcBeat,i];
                PCRhythm[destBeat,i] = PCRhythm[srcBeat,i];
            }

            BeatClassifications[destBeat] = BeatClassifications[srcBeat];
            BeatBegins[destBeat] = BeatBegins[srcBeat];
            BeatEnds[destBeat] = BeatBegins[srcBeat];
            BeatsSinceLastMatch[destBeat] = BeatsSinceLastMatch[srcBeat];
            BeatAmps[destBeat] = BeatAmps[srcBeat];

            // Adjust data in dominant beat monitor.

            classify.AdjustDomData(srcBeat, destBeat);
        }


        /********************************************************************
	        Minimum beat variation returns a 1 if the previous eight beats
	        have all had similarity indexes less than 0.5.
        *********************************************************************/

        public static bool MinimumBeatVariation(int type)
        {
            int i;
            for (i = 0; i < BdacH.MAXTYPES; ++i)
                if (MIs[type,i] > 0.5)
                    i = BdacH.MAXTYPES + 2;
            if (i == BdacH.MAXTYPES)
                return true;
            else
                return false;
        } 


        /**********************************************************************
	        WideBeatVariation returns true if the average similarity index
	        for a given beat type to its template is greater than WIDE_VAR_LIMIT.
        ***********************************************************************/

        public static double WIDE_VAR_LIMIT = 0.50;

        public static bool WideBeatVariation(int type)
        {
            int i, n;
            double aveMI;

            n = BeatCounts[type];
            if (n > 8)
                n = 8;

            for (i = 0, aveMI = 0; i < n; ++i)
                aveMI += MIs[type,i];

            aveMI /= n;
            if (aveMI > WIDE_VAR_LIMIT)
                return true;
            else
                return false;
        }

    }
}
